/*
 * ParamIntegral.java
 * 
 * Copyright 2004 Christoph Csallner and Yannis Smaragdakis.
 */
package edu.gatech.cc.cnc.params;

import de.fhg.first.fd.Domain;
import de.fhg.first.fd.Labeling;
import de.fhg.first.fd.Solver;
import de.fhg.first.fd.UninstantiatedException;
import de.fhg.first.fd.Var;
import de.fhg.first.fd.X;
import edu.gatech.cc.cnc.Out;
import edu.gatech.cc.cnc.ParseFct;
import edu.gatech.cc.cnc.ParseFct2;
import edu.gatech.cc.jcrasher.plans.expr.Expression;
import edu.gatech.cc.jcrasher.plans.expr.Variable;
import edu.gatech.cc.jcrasher.plans.stmt.LocalVariableDeclarationStatement;

/**
 * Represents integral method parameter or other variable.
 * <p>
 * Keeps reference to POOC constraint solver.
 * 
 * @author csallner@gatech.edu (Christoph Csallner)
 */
public abstract class ParamIntegral<T> extends Param<T> {
	
	/* Class param */
	static Solver solver = new de.fhg.first.fd.firstcs.Solver();
	
	protected static Domain intDomain = null;				//set in resetSolver().
	protected static Domain byteDomain = null;
	protected static Domain shortDomain = null;
	protected static Domain booleanDomain = null;
	
	protected static Labeling labeling = null;			//set in prepareSolution().
	
	/**
	 * @return solver for integral variables.
	 */
	public static Solver getSolver() {return solver;}
	
	
	/* Instance param: variable in integer solver. */
	protected Var var = null;

	
	/**
	 * Initialize solver 
	 */
	public static void resetSolver() {
		solver = new de.fhg.first.fd.firstcs.Solver();
		//intDomain = solver.newDomain(Integer.MIN_VALUE, Integer.MAX_VALUE);
		intDomain = solver.newDomain(Domain.INFIMUM, Domain.SUPREMUM);
		byteDomain = solver.newDomain(Byte.MIN_VALUE, Byte.MAX_VALUE);
		shortDomain = solver.newDomain(Short.MIN_VALUE, Short.MAX_VALUE);
		booleanDomain = solver.newDomain(0, 1);			
	}
	
	
	/**
	 * To be called after parsing, where variables and constraints are created,
	 * yet before generating solutions.
	 */
	public static void prepareSolution(ParamIntegral[] wrappers) {
    if (wrappers==null || wrappers.length==0)
      labeling = null;
    
		Var[] vars = new Var[wrappers.length];		
		for (int i = 0; i < wrappers.length; i++) {
			vars[i] = wrappers[i].var;
		}
		labeling = solver.newLabeling(vars);
	}
	
	/**
	 * Generate a new solution for the constrained system previously
	 * prepared via prepareSolution(ParamInt[]).
	 */
	public static boolean generateSolution() {
    if (labeling==null)
      return false;
    
		return labeling.nextSolution();
	}	
	
	
	/**
	 * Map a string representation to a constraint solver constant.
	 */
	public abstract X parse(String constant);
	
	
	/**
	 * Constructor
	 */
	public ParamIntegral(int minValue, int maxValue, String name, Domain domain) {		
		super(name);
		var = solver.newVar(minValue, maxValue);
		solver.in(var, domain);	//every var needs at least one constraint.
	}
	
	/**
	 * @return wrapped value, throws exception otherwise.
	 */
	protected int getValue() {
		try {
			return var.getVal();
		}
		catch(UninstantiatedException e){
			throw new IllegalStateException(e.getMessage());
		}
	}

	/**
	 * @return variable used by underlying int constraint solver.
	 */
	public X getVar() {
		return var;
	}
	
	
	/**
	 * Print a warning
	 * @return null
	 */
	public X parseError(String value) {
		Out.debug(
				"Cannot parse " +value +" as a " +getTypeName()+", " +
						"ignoring constraint " +ParseFct.getCnt());
		return null;
	}


	/**
	 * Print a warning
	 * @return null
	 */
	public X tooLargeForSolver(String value) {
		Out.debug(
				value +" is outside the scope of CnC's int-solver POOC, " +
						"ignoring constraint " +ParseFct.getCnt());
		return null;
	}
	
	
	
	@Override
	public void constrain(String lhs, int r2) {
		X x = parse(lhs);
		if (x!=null) {
			ParseFct2.constrain(x, r2, var);
		}
		//else
		//TODO: Log did not apply constraint as we could not parse lhs.		
	}
	
	@Override
	public void constrain(int r2, String rhs) {
		X x = parse(rhs);
		if (x!=null) {
			ParseFct2.constrain(var, r2, x);
		}
		//else
		//TODO: Log did not apply constraint as we could not parse rhs.
	}
	
	@Override
	public void constrain(int r2, Param<?> rhs) {
		if (rhs instanceof ParamIntegral)
			ParseFct2.constrain(var, r2, ((ParamIntegral)rhs).getVar());
		//else
		//TODO: Log did not apply constraint because rhs not integral.
	}
	
	@Override
	public String getTypeName() {return getType().getName();}
  
  @Override
  public abstract Class<T> getType();
  
  public abstract Expression<T> getPlanExpression();
  
  @Override
  public LocalVariableDeclarationStatement<? extends T> getPlan() {
    Variable<T> variable = nextID(getType());
    Expression<? extends T> value = getPlanExpression();
    return new LocalVariableDeclarationStatement<T>(variable, value);
  }
}
